#include <stdio.h>   /* Standard io */
#include <string.h>  /* String functions */
#include <unistd.h>  /* UNIX standard functions */
#include <fcntl.h>   /* File controls */
#include <errno.h>   /* Error numbers and perror */
#include <termios.h> /* POSIX terminal control */
#include <stdlib.h>  /* Memory management */

#include "reader.h"

/*
 * Opens the serial port file descriptor
 *
 * @return FD, or -1 on error
 */

int open_port() {
  int fd = open( SERIAL_DEVICE_TTY, O_RDWR | O_NOCTTY | O_NDELAY );
  if ( fd == -1 ) {
    fprintf( stderr, "Failed to open serial device %s\n", SERIAL_DEVICE_TTY );
    perror("open_port: ");
  } else {
    fcntl(fd, F_SETFL, 0);
  }  
  return fd;
}

int configure_port( int fd ) {

  struct termios options;
  tcgetattr( fd, &options );
  cfsetispeed( &options, B9600 ); /* set in baud rate */
  cfsetospeed( &options, B9600 ); /* set out baud rate */

  /*set 8N1 */
  options.c_cflag &= ~PARENB;
  options.c_cflag &= ~CSTOPB;
  options.c_cflag &= ~CSIZE;
  options.c_cflag |= CS8;

  options.c_cflag &= ~CRTSCTS; /* no flow control */

  options.c_cflag |= (CLOCAL | CREAD); //enable read and set the local mode

  options.c_oflag |= OPOST; /* set raw output */

  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* set raw input */

  tcsetattr(fd, TCSANOW, &options); /* push the attributes - immediately */

  return 0;
}

int get_device_port() {
  int fd = open_port();
  if ( fd == -1 ) return -1;

  configure_port( fd );

#ifdef VERBOSE
  printf( "READY TO SCAN\n" );
#endif

  return fd;
}

int close_device_port( int fd ) {
  close(fd);
  return 0;
}

int read_card( int fd, char **card_return, int card_return_len ) {
  char *card = *card_return; /* buffer to build the card in */
  char buf[READ_BUFFER_SIZE];  /* read input buffer */
  int c;       /*input char*/
  int  nbytes; /* Number of bytes read */
  int i;       /* Card byte iterator*/
  int card_len = 0;   /* Length of card tracker */

  while ( (nbytes = read(fd, buf, sizeof(buf) )) > 0 ) {

#ifdef EXTRA_VERBOSE
    printf( "I read %i bytes\n", nbytes );
#endif

    for ( i = 0; i < nbytes; i++ ) {
      c = buf[i];

      if ( c == STX ) {
#ifdef EXTRA_VERBOSE
	printf( "GOT START\n" );
#endif
	card_len = 0;
	memset( card, '\0', 300 );

      } else if ( c == ETX ) {
#ifdef EXTRA_VERBOSE
	printf( "GOT END\n" );
#endif
	return card_len;

      } else {
	card[card_len] = c;
	card_len++;

	if ( card_len > card_return_len ) {
	  fprintf( stderr, "ERROR: Card read size overflow!\n" );
	  return 0;
	}
      }

    }

  } /* read loop */
  
  return 0;
}

int read_device_and_print( int fd ) {

  /* buffer to build the card in */
  char *card = (char*)malloc(READ_BUFFER_SIZE*sizeof(char));
  int card_len;

  while ( 1 ) {
    
    card_len = read_card( fd, &card, READ_BUFFER_SIZE*sizeof(char) );

    if ( card_len == 0 ) {
      fprintf( stderr, "ERROR: Failure in card read!\n" );
      free(card);
      return -1;
    }

    /*Print out the card data in different ways*/    
    printf( "CARD(string) = %s\n", card );
    print_card_by_raw_bytes( card, card_len );
    print_card_by_values( card, card_len, 0 );
    print_card_by_values( card, card_len, 1 );
    printf( "\n" );
    
  } /* while(1) loop */
  
  /*NOTREACHED*/
  free(card);
  return -1;
}
int print_card_by_raw_bytes( char *card, int len ) {
  int i;
  printf( "CARD(bytes)  = |" );
  for ( i = 0; i < len; i++ ) {
      printf( "%x|", card[i] );
      fprintf (stderr, "%c", card[i]);
  }
  printf( "\n" );
  return 0;
}  

int print_card_by_values( char *card, int len, int track_nl ) {
  int i;
  int track = 0;

  if ( track_nl ) {
    printf( "CARD (by track) = \n   |" );
  } else {
    printf( "CARD (values) = |" );
  }

  for ( i = 0; i < len; i++ ) {
    char c = card[i];
    
    if ( c == CARD_TRACK1_SS ) {
      printf("<T1_SS>|");
      track = 1;
    } else if ( c == CARD_TRACK2_SS && track == 1 ) {
      printf("<T2_SS>|");
      track = 2;
    } else if ( c == CARD_TRACK3_SS && track == 2 ) {
      printf("<T3_SS>|");
      track = 3;
    } else if ( c == CARD_ES ) {
      printf( "<ES>|" );
    } else if ( c == CARD_DLE ) {
      if ( track_nl ) printf( "\n   |<DLE>|\n   " );
      else printf( "<DLE>|" );
    } else { 
      printf( "%x|", c );
    }
  }
  printf( "\n" );

  return 0;
}

int parse_card_as_credit_card( char *card, int len, credit_card_data *data ) {
  if ( data == NULL ) return 1;
  memset( data, '\0', sizeof(credit_card_data ) );

  if ( len < 15 ) {
    fprintf( stderr, "ERROR: Card input is impossibly small!\n" );
    return 1;
  }

  if ( card[0] != CARD_TRACK1_SS ) {
    fprintf( stderr, "ERROR: Invalid start value: %x\n", card[0] );
    return 1;
  }

  int i = 1, j;
  data->format_code = card[i];
  i++;

  for ( j=0; card[i] != CARD_CC_SEPARATOR && i < len; i++,j++ ) {
    data->account_num[j] = card[i];
  }
  i++;

  for ( j=0; card[i] != CARD_CC_SEPARATOR && i < len; i++,j++ ) {
    data->name[j] = card[i];
  }
  i++;

  if ( card[i] == CARD_CC_SEPARATOR ) { 
    i++; /*can be no date*/
  } else {
    for ( j=0; j < 4 && i < len; i++,j++ ) {
      data->exp[j] = card[i];
    }
  }

  if ( card[i] == CARD_CC_SEPARATOR ) { 
    i++; /*can be no service code*/
  } else {
    for ( j=0; j < 3 && i < len; i++,j++ ) {
      data->service_code[j] = card[i];
    }
  }
  for ( j=0; card[i] != CARD_ES && i < len; i++,j++ ) {
    data->discretionary_data[j] = card[i];
  }

  return 0;
}
int print_credit_card( credit_card_data *data ) {
  if ( data == NULL ) return 1;
  printf( "FORMAT_CODE = %c\n", data->format_code );
  printf( "NAME = %s\n", data->name );
  printf( "ACCOUNT = %s\n", data->account_num );
  printf( "EXP = %s\n", data->exp );
  printf( "SERVICE_CODE = %s\n", data->service_code );
  printf( "DISCRETIONARY = %s\n", data->discretionary_data );

  return 0;
}
